package main.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Diagram {
    private String name;
	private List<Relation> relations;
	private List<Entity> entities;
	private List<Hierarchy> hierarchies;
	
	public Diagram(String name){
		this.name = name;
		this.setRelations(new ArrayList<Relation>());
		this.setEntities(new ArrayList<Entity>());
		this.hierarchies = new ArrayList<Hierarchy>();
	}

	public List<Relation> getRelations() {
		return relations;
	}

	public void setRelations(List<Relation> relations) {
		if (relations == null){
			this.relations = new ArrayList<Relation>();
		} else {
			this.relations = relations;
		}
	}

	public List<Entity> getEntities() {
		return entities;
	}

	public void setEntities(List<Entity> entities) {
		if (entities == null){
			this.entities = new ArrayList<Entity>();
		} else {
			this.entities = entities;
		}
	}
	
	public void addRelation(Relation r){
		getRelations().add(r);
	}
	
	public void addEntity(Entity e){
		getEntities().add(e);
	}

	public void removeRelation(Relation r){
		getRelations().remove(r);
	}
	
	public void removeEntity(Entity e){
		getEntities().remove(e);
	}
	
	public String getName(){
		return name;
	}
	
	public void setName(String newName){
		this.name =  newName;
	}
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Mostrando diagrama: "+getName()+"\n\n");
		sb.append("Mostrando Relaciones:\n");
		for (Relation r : this.relations) {
			sb.append("Relacion: " + r.getName() + "\n");
			sb.append("\tEs Composicion: " + r.isComposition().toString() + "\n");
			for (Entity e : r.getEntities()) {
				sb.append("\tEntidad de " + r.getName() + " : " + e.getName() + "\n");
				Cardinality c = r.getCardinalityForEntity(e);
				if(c!=null){
					sb.append("\tCardinalidad min: " + c.getMin() + " max: " + c.getMax() + "\n");
				}
			}
			for (Attribute a : r.getIdAttributes()) {
				sb.append("\tAtributosId de " + r.getName() + " : " + a.getName() + "\n");
				sb.append("\tTipo :" + a.getType() + "\n");
				sb.append(a.toString());
			}
			for (Attribute a : r.getAttributes()) {
				sb.append("\tAtributos de " + r.getName() + " : " + a.getName() + "\n");
				sb.append("\tTipo :" + a.getType() + "\n");
				sb.append(a.toString());
			}
		}
		sb.append("\nMostrando Entidades:\n\t\t");
		for (Entity e : this.entities) {
			sb.append("Entidad: " +e.getName() + "\n\t\t");
			sb.append("\tEs Master: " +e.isMasterEntity().toString() + "\n\t\t");
			for (Attribute a : e.getIdAttributes()) {
				sb.append("\tAtributosId de " + e.getName() + " : " + a.getName() + "\n");
				sb.append("\tTipo :" + a.getType() + "\n");
				sb.append(a.toString());
			}
			for (Attribute a : e.getAttributes()) {
				sb.append("\tAtributos de " + e.getName() + " : " + a.getName() + "\n");
				sb.append("\tTipo :" + a.getType() + "\n");
				sb.append(a.toString());
			}
		}
		sb.append("\nMostrando Jerarquias:\n\t\t");
		if ( this.hierarchies != null ){
			for (Hierarchy h : this.hierarchies) {
				sb.append("Jerarquia: " +h.getName() + "\n\t\t");
				sb.append("Tipo: " + h.getType()+ "\n\t\t");
				Entity parent = h.getParent();
				sb.append("Entidad padre: " + parent.getName()+ "\n\t\t");
				sb.append("Entidades hijas"+ "\n\t\t");
				for (Entity e : h.getEntities()){
					sb.append("\tEntidad hija: " + e.getName() + "\n\t\t");
				}
			}
		}
		sb.append("Fin del diagrama");
		return sb.toString();
	}
	
	public List<String> getEntitiesNames(){
		List<String> names = new ArrayList<String>();
		for (Entity e : this.entities) {
			names.add(e.getName());
		}
		return names;
	}
	
	public List<String> getOwnEntitiesNames(){
		List<String> names = new ArrayList<String>();
		for (Entity e : this.entities) {
			if (e.getDiagramParent().equals(this.getName())){
				names.add(e.getName());
			}
		}
		return names;
	}
	
	
	public List<Relation> removeComponent(main.model.Component component) {
		List<Relation> relationsToRemove = null;
		if (component.isEntity()){
			Iterator<Relation> iterator = this.getRelations().iterator();
			relationsToRemove = new ArrayList<Relation>();
			while (iterator.hasNext()){
				Relation next = iterator.next();
				if (next.getEntities().contains(component)){
					relationsToRemove.add(next);
				}
			}
			this.entities.remove(component);
		} else if (component.isRelation()){
			this.relations.remove(component);
		} else if (component.isAttribute()){
			Component parent = ((Attribute)component).getParent();
			parent.getAttributes().remove(component);
			parent.getIdAttributes().remove(component);
		} else {
			this.hierarchies.remove(component);
		}
		return relationsToRemove;
	}
	
	public List<Hierarchy> getHierarchiesToRemove(Component component) {
		List<Hierarchy> hierarchiesToRemove = new ArrayList<Hierarchy>();
		if (component.isEntity()){
			Iterator<Hierarchy> hierarchies = this.getHierarchies().iterator();
			while (hierarchies.hasNext()){
				Hierarchy next = hierarchies.next();
				if (next.getParent().equals(component)){
					hierarchiesToRemove.add(next);
				} else if (next.getEntities().contains(component)){
					next.getEntities().remove(component);
					if (next.getEntities().isEmpty()){
						hierarchiesToRemove.add(next);
					}
				}
			}
		}
		return hierarchiesToRemove;
	}

	public main.model.Component getComponentByName(String nodeName) {
		Iterator<Entity> eIterator = this.entities.iterator();
		Boolean found = false;
		Component component = null;
		while (eIterator.hasNext() && !found){
			Entity e = eIterator.next();
			if (e.getName().equals(nodeName)){
				found = true;
				component = e;
			}
		}
		if (!found){
			Iterator<Relation> rIterator = this.relations.iterator();
			while (rIterator.hasNext() && !found){
				Relation r = rIterator.next();
				if (r.getName().equals(nodeName)){
					found = true;
					component = r;
				}
			}
		}
		if (!found){
			Iterator<Hierarchy> hIterator = this.hierarchies.iterator();
			while (hIterator.hasNext() && !found){
				Hierarchy h = hIterator.next();
				if (h.getName().equals(nodeName)){
					found = true;
					component = h;
				}
			}
		}
		if (!found){
			component = this.findAttributeByName(nodeName);
		}
		return component;
	}
	
	private Component findAttributeByName(String nodeName) {
		Boolean found = Boolean.FALSE;
		Iterator<Entity> eIt = this.entities.iterator();
		main.model.Component component = null;
		while (eIt.hasNext() && !found){
			Entity entity = eIt.next();
			Iterator<Attribute> attIt = entity.getAttributes().iterator();
			while (!found && attIt.hasNext()){
				Attribute att = attIt.next();
				component = proccessAtt(found, att, nodeName);
			}
			Iterator<Attribute> idAttsIt = entity.getIdAttributes().iterator();
			while (!found && idAttsIt.hasNext()){
				Attribute att = idAttsIt.next();
				component = proccessAtt(found, att, nodeName);
			}
		}
		if (!found){
			Iterator<Relation> rIt = this.relations.iterator();
			while (rIt.hasNext() && !found){
				Relation r = rIt.next();
				Iterator<Attribute> attIt = r.getAttributes().iterator();
				while (!found && attIt.hasNext()){
					Attribute att = attIt.next();
					component = proccessAtt(found, att, nodeName);
				}
				Iterator<Attribute> idAttsIt = r.getIdAttributes().iterator();
				while (!found && idAttsIt.hasNext()){
					Attribute att = idAttsIt.next();
					component = proccessAtt(found, att, nodeName);
				}
			}
		}
		return component;
	}

	private main.model.Component proccessAtt(Boolean found, Attribute att, String name) {
		main.model.Component component = null;
		if (att.getName().equals(name)){
			found = Boolean.TRUE;
			component = att;
		} else {
			Iterator<Attribute> attIt = att.getAttributes().iterator();
			while (!found && attIt.hasNext()){
				Attribute next = attIt.next();
				if (next.getName().equals(name)){
					found = Boolean.TRUE;
					component = next;
				}
			}
		}
		return component;
	}

	public Entity getEntityByName(String name){
		Iterator<Entity> eIterator = this.entities.iterator();
		Boolean found = false;
		Entity component = null;
		while (eIterator.hasNext() && !found){
			Entity e = eIterator.next();
			if (e.getName().equals(name)){
				found = true;
				component = e;
			}
		}
		return component;
	}

	public List<Hierarchy> getHierarchies() {
		return hierarchies;
	}

	public void setHierarchies(List<Hierarchy> hierarchies) {
		if (hierarchies == null){
			this.hierarchies = new ArrayList<Hierarchy>();
		} else {
			this.hierarchies = hierarchies;
		}
	}

	public void addHierarchy(Hierarchy h) {
		this.hierarchies.add(h);
	}
	
	public void removeHierarchy(Hierarchy h) {
		this.hierarchies.remove(h);
	}
	
	public List<String> getDiagramLeaves(){
		List<String> l = new ArrayList<String>();
		l.addAll(this.getEntitiesNames());
		for (Relation rel : this.relations) {
			l.add(rel.getName());
		}
		for (Hierarchy h : this.hierarchies) {
			l.add(h.getName());
		}
		return l;
	}

	public List<Relation> getRelationsFromEntity(Entity e) {
		List<Relation> relations = new ArrayList<Relation>();
		for (Relation r : this.getRelations()) {
			if (r.getEntities().contains(e)){
				relations.add(r);
			}
		}
		return relations;
	}

	public List<Relation> getExistingRelations(List<Relation> relationsFromEntity) {
		List<Relation> existingRelations = new ArrayList<Relation>();
		for (Relation relation : relationsFromEntity) {
			if (!this.relations.contains(relation)){
				Boolean isValidRelation = Boolean.TRUE;
				Iterator<Entity> entitiesIt = relation.getEntities().iterator();
				while (isValidRelation && entitiesIt.hasNext()){
					Entity e = entitiesIt.next();
					if (this.getEntityByName(e.getName()) == null){
						isValidRelation = Boolean.FALSE;
					}
				}
				if (isValidRelation){
					existingRelations.add(relation);
				}
			}
		}
		return existingRelations;
	}

	public List<Hierarchy> getExistingHierarchies(List<Hierarchy> hierarchiesFromEntity) {
		List<Hierarchy> existingHierarchies = new ArrayList<Hierarchy>();
		for (Hierarchy hierarchy : hierarchiesFromEntity) {
			if (!this.hierarchies.contains(hierarchy)){
				Boolean isValidRelation = Boolean.TRUE;
				Iterator<Entity> entitiesIt = hierarchy.getEntities().iterator();
				while (isValidRelation && entitiesIt.hasNext()){
					Entity e = entitiesIt.next();
					if (this.getEntityByName(e.getName()) == null){
						isValidRelation = Boolean.FALSE;
					}
				}
				if (isValidRelation){
					if (this.getEntityByName(hierarchy.getParent().getName()) != null){
						existingHierarchies.add(hierarchy);
					}
				}
			}
		}
		return existingHierarchies;
	}

	public List<Hierarchy> getHierarchiesFromEntity(Entity e) {
		List<Hierarchy> hierarchies = new ArrayList<Hierarchy>();
		for (Hierarchy h : this.getHierarchies()) {
			if (h.getEntities().contains(e) || h.getParent().equals(e)){
				hierarchies.add(h);
			}
		}
		return hierarchies;
	}
	
}